/** Copyright 2015 TappingStone, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
  *     http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */

package io.prediction.tools.dashboard

// Reference from: https://gist.github.com/waymost/4b5598523c2c7361abea

import spray.http.{HttpMethods, HttpMethod, HttpResponse, AllOrigins}
import spray.http.HttpHeaders._
import spray.http.HttpMethods._
import spray.http.HttpEntity
import spray.routing._
import spray.http.StatusCodes
import spray.http.ContentTypes

// see also https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS
trait CORSSupport {
  this: HttpService =>
  
  private val allowOriginHeader = `Access-Control-Allow-Origin`(AllOrigins)
  private val optionsCorsHeaders = List(
    `Access-Control-Allow-Headers`("""Origin,
                                      |X-Requested-With,
                                      |Content-Type,
                                      |Accept,
                                      |Accept-Encoding,
                                      |Accept-Language,
                                      |Host,
                                      |Referer,
                                      |User-Agent""".stripMargin.replace("\n", " ")),
    `Access-Control-Max-Age`(1728000)
  )
 
  def cors[T]: Directive0 = mapRequestContext { ctx => 
    ctx.withRouteResponseHandling {
      // OPTION request for a resource that responds to other methods
      case Rejected(x) if (ctx.request.method.equals(HttpMethods.OPTIONS) && 
          x.exists(_.isInstanceOf[MethodRejection])) => {
        val allowedMethods: List[HttpMethod] = x.collect { 
          case rejection: MethodRejection => rejection.supported
        }
        ctx.complete {
          HttpResponse().withHeaders(
            `Access-Control-Allow-Methods`(HttpMethods.OPTIONS, allowedMethods :_*) :: 
            allowOriginHeader ::
            optionsCorsHeaders
          )
        }
      }
    }.withHttpResponseHeadersMapped { headers =>
      allowOriginHeader :: headers
    }
  }
  
  override def timeoutRoute: StandardRoute = complete {
    HttpResponse(
      StatusCodes.InternalServerError,
      HttpEntity(ContentTypes.`text/plain(UTF-8)`, 
          "The server was not able to produce a timely response to your request."),
      List(allowOriginHeader)
    )
  }
}
